package cn.pilipa.common.config;

import cn.pilipa.common.config.swagger.ProxiedControllerRequestMappingHandlerMapping;
import cn.pilipa.common.config.swagger.Swagger2ControllerRequestMappingHandlerMapping;
import cn.pilipa.common.config.swagger.SwaggerProperties;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
import org.javamoney.moneta.Money;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiResourceController;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import springfox.documentation.swagger2.web.Swagger2Controller;

@Configuration
@EnableSwagger2
@ConditionalOnProperty(prefix = "swagger", name = "enable")
@EnableConfigurationProperties(SwaggerProperties.class)
@Slf4j
public class SwaggerConfig implements WebMvcConfigurer {

  @Autowired
  private SwaggerProperties swaggerProperties;
  /**
   * swagger 配置.
   * @return
   */
  @Bean
  public Docket api() {
    final Docket docket = new Docket(DocumentationType.SWAGGER_2)
        .select()
        .apis(RequestHandlerSelectors.any())
        .paths(PathSelectors.any())
        .build()
        .enable(swaggerProperties.getEnable())
        .apiInfo(apiInfo());
    try {
      if (Class.forName("org.javamoney.moneta.Money") != null)
        docket.directModelSubstitute(Money.class, String.class);
    } catch (ClassNotFoundException e) {
      logger.info("Do not add money substitute due to missing money library.");
    }
    return  docket;
  }

  private ApiInfo apiInfo() {
    return new ApiInfo(
        swaggerProperties.getTitle(),
        swaggerProperties.getDesc(),
        swaggerProperties.getVersion(),
        swaggerProperties.getServiceUrl(),
        new Contact(swaggerProperties.getContact(), swaggerProperties.getContactWeb(), swaggerProperties.getContactEmail()),
        swaggerProperties.getLicense(),
        swaggerProperties.getLicenseUrl(),
        Collections.emptyList());
  }

  /**
   * HandlerMapping.
   * @param environment environment
   * @param documentationCache documentationCache
   * @param mapper mapper
   * @param jsonSerializer jsonSerializer
   * @return
   */
  @Bean
  public HandlerMapping swagger2ControllerProxyMapping(
      Environment environment,
      DocumentationCache documentationCache,
      ServiceModelToSwagger2Mapper mapper,
      JsonSerializer jsonSerializer) {

    return new Swagger2ControllerRequestMappingHandlerMapping(
        environment,
        new Swagger2Controller(environment, documentationCache, mapper, jsonSerializer),
        swaggerProperties.getBasePath());
  }

  /**
   * HandlerMapping.
   * @param apiResourceController apiResourceController
   * @return
   */
  @Bean
  public HandlerMapping apiResourceControllerProxyMapping(
      ApiResourceController apiResourceController) {

    return new ProxiedControllerRequestMappingHandlerMapping(
        apiResourceController,
        swaggerProperties.getBasePath());
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {

    String basePath = swaggerProperties.getBasePath();

    registry
        .addResourceHandler(basePath + "/swagger-ui.html**")
        .addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
    registry
        .addResourceHandler(basePath + "/webjars/**")
        .addResourceLocations("classpath:/META-INF/resources/webjars/");
  }
}
